﻿using System;
using FT.Architecture.Exceptions;
using FT.Architecture.NHibernate.Factories;
using FT.Architecture.NHibernate.SessionManagement;
using FT.Architecture.UnitOfWork;
using NHibernate;

namespace FT.Architecture.NHibernate.Context
{
    public class NHibernateStatelessUnitOfWork : IStatelessUnitOfWork
    {
        private readonly ISessionProviderService _sessionProviderService;
        private ISessionFactory _factory;
        private static NHibernateSessionManager _sessionManager;

        public NHibernateStatelessUnitOfWork(ISessionProviderService sessionProviderService, INHibernateConfigurator nHibernateConfigurator)
        {
            _sessionProviderService = sessionProviderService;
            if (_sessionManager == null)
                _sessionManager = NHibernateSessionManager.GetInstance(nHibernateConfigurator);
        }

        public void Initialise()
        {
            if (_sessionProviderService == null)
            {
                throw new UnitOfWorkException("The session provider service is not initialised", this);
            }

            _sessionProviderService.SetStatelessSession(null);
            _factory = _sessionManager.GetSessionFactoryFor("sf");
        }

        public IUnitOfWorkInstance Start()
        {
            if (_factory == null)
            {
                Initialise();
            }

            if (_factory == null)
            {
                throw new UnitOfWorkException("The NHibernate session factory is not initialised", this);
            }

            IStatelessSession session = _factory.OpenStatelessSession();

            if (session == null)
            {
                throw new UnitOfWorkException("The NHibernate session is not initialised", this);
            }

            session.BeginTransaction();

            if (_sessionProviderService == null)
            {
                throw new UnitOfWorkException("The session provider service is not initialised", this);
            }

            _sessionProviderService.SetStatelessSession(session);

            return new StatelessUnitOfWorkInstance(this);
        }

        public void Commit()
        {
            if (_sessionProviderService == null)
            {
                throw new UnitOfWorkException("The session provider service is not initialised", this);
            }

            var session = _sessionProviderService.GetStatelessSession();

            if (session != null)
            {
                ITransaction transaction = session.Transaction;
                if (transaction.IsActive)
                {
                    transaction.Commit();
                }
                session.Dispose();
                _sessionProviderService.SetStatelessSession(null);
            }
        }

        public void Rollback()
        {
            if (_sessionProviderService == null)
            {
                throw new UnitOfWorkException("The session provider service is not initialised", this);
            }

            var session = _sessionProviderService.GetStatelessSession();

            if (session != null)
            {
                ITransaction transaction = session.Transaction;
                if (transaction.IsActive)
                {
                    transaction.Rollback();
                }
                session.Dispose();
                _sessionProviderService.SetStatelessSession(null);
            }
        }

        public void Flush()
        {
            throw new NotSupportedException("Flushing is not supported on a stateless session");
        }

        public bool IsStarted()
        {
            if (!_sessionProviderService.HasStatelessSession())
                return false;

            var session = _sessionProviderService.GetStatelessSession();
            return session.IsOpen;
        }
    }
}
